package banshee.ai;

import java.util.ArrayList;

import jp.ac.waseda.cs.washi.game.api.CharaInfo;
import jp.ac.waseda.cs.washi.game.api.GameInfo;
import jp.ac.waseda.cs.washi.game.api.MapInfo;
import jp.ac.waseda.cs.washi.game.api.PumpkinCoreInfo;
import jp.ac.waseda.cs.washi.game.api.PumpkinInfo;
import jp.ac.waseda.cs.washi.gameaiarena.api.Point2;

public class RawMap
{
	// public int map[][];
	public GameInfo			gameInfo;
	public CharaInfo		player;
	public CharaInfo		enemy;
	public MapInfo			info;
	public MapInfoUtil		util;
	public ArrayList<Point2>	playerKingPumpkinCenterLocations;
	public ArrayList<Point2>	playerMiniPumpkinCenterLocations;
	public ArrayList<Point2>	enemyKingPumpkinCenterLocations;
	public ArrayList<Point2>	enemyMiniPumpkinCenterLocations;

	public RawMap(GameInfo gameInfo)
	{
		this.gameInfo = gameInfo;
		info = gameInfo.getMap();
		// int w = info.getWidth();
		// int h = info.getHeight();
		// map = new int[w][h];
		player = gameInfo.getPlayer();
		enemy = gameInfo.getEnemy();
		util = new MapInfoUtil(info);
	}

	public void refresh()
	{
		// int w = info.getWidth();
		// int h = info.getHeight();
		// for (int i = 0; i < w; i++) {
		// for (int j = 0; j < h; j++) {
		// map[i][j] = util.getStrength(player, i, j);
		// }
		// }
		playerMiniPumpkinCenterLocations = toLocation(player.getMiniPumpkins());
		playerKingPumpkinCenterLocations = toLocation(player.getKingPumpkins());
		enemyKingPumpkinCenterLocations = toLocation(enemy.getKingPumpkins());
		enemyMiniPumpkinCenterLocations = toLocation(enemy.getMiniPumpkins());
		// System.out.println(player.getMiniPumpkinCenterLocations().size());
		// System.out.println(player.getPumpkins().size());
//		System.out
//				.println("############################################################"
//						+ playerMiniPumpkinCenterLocations.size());
	}

	private ArrayList<Point2> toLocation(ArrayList<PumpkinInfo> pumpkinInfos)
	{
		ArrayList<Point2> result = new ArrayList<Point2>();
		for (PumpkinInfo v : pumpkinInfos) {
			result.add(v.getLocation().add(new Point2(2,2)));
			//System.out.println(v.getLocation().add(new Point(2, 2)));
		}
		return result;
	}

	public StrategyResult breakMini(Point2 p)
	{
		return new StrategyResult(p, 30,
				info.getPath(player.getLocation(), p).length + 20);
	}

	public StrategyResult breakKing(Point2 p)
	{
		return new StrategyResult(p, 106,
				info.getPath(player.getLocation(), p).length + 84);
	}

	public StrategyResult createComplete(Point2 p)
	{
		if (util.getStrength(player, p) == MapInfoUtil.WALL) {
			return StrategyResult.NO_WAY;
		}
		// Mini enemy
		for (Point2 v : enemyMiniPumpkinCenterLocations) {
			if (distance(p, v) <= 1) {
				return StrategyResult.NO_WAY;
			}
		}

		// King enemy
		for (Point2 v : enemyKingPumpkinCenterLocations) {
			if (distance(p, v) <= 2) {
				return StrategyResult.NO_WAY;
			}
		}

		// Mini player
		for (Point2 v : playerMiniPumpkinCenterLocations) {
			if (distance(p, v) <= 2) {
				return StrategyResult.NO_WAY;
			}
		}

		// King player
		for (Point2 v : playerKingPumpkinCenterLocations) {
			if (distance(p, v) <= 3) {
				return StrategyResult.NO_WAY;
			}
		}
		return new StrategyResult(p, 2 - score(util.getStrength(player, p)),
				spellCost4(p) + info.getPath(player.getLocation(), p).length);
	}

	private static final Point2	KING_STR_0[]	= new Point2[] {
			new Point2(-3, 0), new Point2(-2, -1), new Point2(-2, 1),
			new Point2(-1, -2), new Point2(-1, 2), new Point2(0, -3),
			new Point2(0, 3), new Point2(1, -2), new Point2(1, 2),
			new Point2(2, -1), new Point2(2, 1), new Point2(3, 0), };

	private static final Point2	KING_STR_4[]	= new Point2[] {
			new Point2(-2, 0), new Point2(-1, -1), new Point2(-1, 0),
			new Point2(-1, 1), new Point2(0, -2), new Point2(0, -1),
			new Point2(0, 0), new Point2(0, 1), new Point2(0, 2),
			new Point2(1, -1), new Point2(1, 0), new Point2(1, 1),
			new Point2(2, 0),					};

	private static final Point2	MINI_STR_0[]	= new Point2[] {
			new Point2(-2, 0), new Point2(-1, -1), new Point2(-1, 1),
			new Point2(0, -2), new Point2(0, 2), new Point2(2, 0), };

	private static final Point2	MINI_STR_4[]	= new Point2[] {
			new Point2(-1, 0), new Point2(0, -1), new Point2(0, 0),
			new Point2(0, 1), new Point2(1, 0),	};

	public StrategyResult createKing(Point2 p)
	{
		if (!checkContainPumpkin4king(p)) {
			return StrategyResult.NO_WAY;
		}
		int miniMap[][] = new int[7][7];
		int cost = 0;
		int gain = 0;
		for (Point2 v : KING_STR_0) {
			Point2 dest = p.add(v);
			gain -= util.getStrength(player, dest);
			miniMap[v.x][v.y] = spellCost0(dest);
			cost -= miniMap[v.x][v.y];
		}

		for (Point2 v : KING_STR_4) {
			Point2 dest = p.add(v);
			gain += 2 - util.getStrength(player, dest);
			miniMap[v.x][v.y] = spellCost4(dest);
			cost += miniMap[v.x][v.y];
		}
		gain += 80;
		cost += info.getPath(player.getLocation(), p).length + 14;
		return new StrategyResult(p, gain, cost);
	}

	public StrategyResult createMini(Point2 p)
	{
		if (util.getStrength(player, p) == MapInfoUtil.WALL) {
			return StrategyResult.NO_WAY;
		}
		if (!checkContainPumpkin4mini(p)) {
			return StrategyResult.NO_WAY;
		}
		// int miniMap[][] = new int[5][5];
		int cost = 0;
		int gain = 0;
		for (Point2 v : MINI_STR_0) {
			Point2 dest = p.add(v);
			int strength = util.getStrength(player, dest);
			// System.out.println(strength);
			if (strength != MapInfoUtil.WALL) {
				gain -= score(strength);
			}
			// miniMap[v.x][v.y] = spellCost0(dest);
			cost -= spellCost0(dest);
		}

		for (Point2 v : MINI_STR_4) {
			Point2 dest = p.add(v);
			int strength = util.getStrength(player, dest);
			if (strength != MapInfoUtil.WALL) {
				gain += 2 - score(strength);
			} else {
				return StrategyResult.NO_WAY;
			}
			// miniMap[v.x][v.y] = spellCost4(dest);
			cost += spellCost4(dest);
		}
		cost += info.getPath(player.getLocation(), p).length + 5;
		return new StrategyResult(p, gain + 20, cost);
		// return StrategyResult.NO_WAY;
	}

	private boolean checkContainPumpkin4mini(Point2 p)
	{
		// System.out.println("size: "+playerMiniPumpkinCenterLocations.size());
		// Mini enemy
		for (Point2 v : enemyMiniPumpkinCenterLocations) {
//			System.out.println("" + p + v + distance(p, v));
			if (distance(p, v) <= 1) {
				// System.out.println("false");
				return false;
			}
		}

		// King enemy
		for (Point2 v : enemyKingPumpkinCenterLocations) {
//			System.out.println("" + p + v + distance(p, v));
			if (distance(p, v) <= 3) {
				return false;
			}
		}

		// Mini player
		for (Point2 v : playerMiniPumpkinCenterLocations) {
//			System.out.println("" + p + v + distance(p, v));
			if (distance(p, v) <= 3) {
				return false;
			}
		}

		// King player
		for (Point2 v : playerKingPumpkinCenterLocations) {
//			System.out.println("" + p + v + distance(p, v));
			if (distance(p, v) <= 4) {
				return false;
			}
		}
		// System.out.println("true");
		return true;
	}

	private boolean checkContainPumpkin4king(Point2 p)
	{

		// Mini enemy
		for (Point2 v : enemyMiniPumpkinCenterLocations) {
			if (distance(p, v) <= 3) {
				return false;
			}
		}

		// King enemy
		for (Point2 v : enemyKingPumpkinCenterLocations) {
			if (distance(p, v) <= 5) {
				return false;
			}
		}

		// Mini player
		for (Point2 v : playerMiniPumpkinCenterLocations) {
			if (distance(p, v) <= 5) {
				return false;
			}
		}

		// King player
		for (Point2 v : playerKingPumpkinCenterLocations) {
			if (distance(p, v) <= 6) {
				return false;
			}
		}
		return true;
	}

	int spellCost4(Point2 p)
	{
		int strength = util.getStrength(player, p);
		if (strength == MapInfoUtil.WALL) {
			return 100000;
		}
		if (strength <= 0) {
			return 2 - strength;
		}
		switch (strength) {
		case 1:
			return 2;
		case 2:
			return 1;
		case 3:
			return 1;
		default:
			return 0;
		}
	}

	int spellCost0(Point2 p)
	{
		int strength = util.getStrength(player, p);
		if (strength <= 0) {
			return 0;
			// return strength;
		}
		return -1;
	}

	private int score(int strength)
	{
		if (strength == 4) {
			return 2;
		}
		if (strength == -4) {
			return -2;
		}
		if (strength < 0) {
			return -1;
		}
		if (strength > 0) {
			return 1;
		}
		return 0;
	}

	private int distance(Point2 p1, Point2 p2)
	{
		return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
	}
}
